package gbench.sandbox.zk.zoo;

import static gbench.common.tree.LittleTree.Term.FT;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.util.Optional;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;

import com.fasterxml.jackson.databind.ObjectMapper;

import gbench.common.matlib.algebra.lisp.Tuple2;
import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.Json;

/**
 * 
 * @author Administrator
 *
 * @param <T>
 */
public interface IZKSession<T> {

    /**
     * 获取具体的实现类
     * 
     * @return
     */
    public T self();

    /**
     * 
     */
    public void start();

    /**
     * 
     */
    public void close();

    /**
     * 
     * @return
     */
    public boolean isStarted();

    public IZKSession<T> addWatcher(final String path, Watcher watcher);

    /**
     * 
     * @param <U>
     * @param path
     * @param mapper
     * @return
     * @throws Exception
     */
    public <U> U get_data_throws(String path, final ExceptionalFunction<byte[], U> mapper) throws Exception;

    /**
     * 
     * @param path
     * @return
     */
    public LinkedHashMap<String, Object> get_children_throws(final String path) throws Exception;

    /**
     * 
     * @param path
     * @param json
     * @throws Exception
     */
    public void create_throws(CreateMode mode, final String path, String json) throws Exception;

    /**
     * 
     * @return
     */
    public boolean exists_throws(final String path) throws Exception;

    /**
     * 
     * @return
     */
    public void delete_path_throws(final String path) throws Exception;

    public void set_data_throws(final String path, final IRecord data) throws Exception;

    public boolean is_ephemeral_throws(final String path) throws Exception;

    /**
     * 
     * @param <T>
     * @param path
     * @param kvs  键值序列
     */
    <X> void update(final String path, @SuppressWarnings("unchecked") X... kvs);

    /**
     * 
     * @param path
     * @return
     * @throws Exception
     */
    default byte[] get_data_throws(final String path) throws Exception {
        return this.get_data_throws(path, e -> e);
    }

    /**
     * 
     * @param <X>
     * @param path
     * @param mapper
     * @return
     * @throws Exception
     */
    default <X> X get_parent_data_throws(final String path, final Function<byte[], X> mapper) throws Exception {
        final var parent_path = IZKSession.getParent(path);
        final var dd = this.get_data_throws(parent_path);
        final var value = mapper.apply(dd);
        return value;
    }

    /**
     * 根节点树
     * 
     * @param rootpath 根节点路径
     * @return 根节点树
     * @throws Exception
     */
    default ZKTrieNode<String> get_tree_throws() throws Exception {
        return this.get_tree_throws("/");
    }

    /**
     * 根节点树
     * 
     * @param rootpath 根节点路径
     * @return 根节点树
     */
    default ZKTrieNode<String> get_tree_throws(final String rootpath) throws Exception {
        final var rootNode = new ZKTrieNode<String>(rootpath);
        final var stack = new Stack<Tuple2<String, ZKTrieNode<String>>>(); // path, zktnode

        final var root_data = this.get_data_throws(rootpath,
                bb -> IRecord.REC(new ObjectMapper().readValue(Optional.ofNullable(new String(bb)) // 引入局部命名变量
                        .map(e -> e.matches("^\\s*\\{.+}\\s*$") ? e : "{}").get(), Map.class))); // 根节点属性
        rootNode.addAttributes(root_data);
        stack.push(Tuple2.TUP2(rootpath, rootNode)); // 根节点

        while (!stack.empty()) {
            final var tuple = stack.pop(); // path and zktnode
            final var parentNode = tuple._2; // 父节点
            final var parentPath = tuple._1; // 父节点路径
            final var kvps = this.get_children_throws(parentPath).entrySet(); // 子节点
            for (var kvp : kvps) {
                final var separator = parentPath.endsWith("/") ? "" : "/"; // 间隔符
                final var childPath = FT("$0$1$2", parentPath, separator, kvp.getKey()); // 子节点路径
                final var name = kvp.getKey(); // 节点名
                final var node = parentNode.addPart(name); // 子节点
                final var data = kvp.getValue(); // 子节点数据
                final var attrs = data instanceof Map ? IRecord.REC(data) : IRecord.REC(); // 属性值

                node.addAttributes(attrs); // 添加属性
                stack.push(Tuple2.TUP2(childPath, node)); // 子节点入栈
            } // for
        } // while

        return rootNode;
    }

    /**
     * 根节点树
     * 
     * @param rootpath 根节点路径
     * @return 根节点树
     * @throws Exception
     */
    default Stream<ZKTrieNode<String>> get_tree_stream_throws(final String rootpath) throws Exception {
        return this.get_tree_throws(rootpath).stream();
    }

    /**
     * 
     * @param path
     * @param json
     * @throws Exception
     */
    default void create_persistent_throws(final String path, final String json) throws Exception {
        this.create_throws(CreateMode.PERSISTENT, path, json);
    }

    /**
     * 
     * @param path
     * @param json
     * @throws Exception
     */
    default void create_ephemeral_throws(final String path, final String json) throws Exception {
        this.create_throws(CreateMode.EPHEMERAL, path, json);
    }

    /**
     * 
     * @param path
     * @param json
     */
    default void create_persistent(final String path, final String json) {
        try {
            this.create_persistent_throws(path, json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 
     * @param path
     * @param json
     */
    default void create_persistent(final String path, IRecord rec) {
        final var json = rec.json();
        this.create_persistent(path, json);
    };

    /**
     * 
     * @param path 绝对路径
     */
    default void deletePath(final String path) {
        try {
            this.delete_path_throws(path);
        } catch (Exception e) {

        }
    }

    /**
     * 
     * @param path
     * @param json
     */
    default void ephemeralOf(final String path, final String json) {
        try {
            this.create_ephemeral_throws(path, json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 
     * @param path
     * @param json
     */
    default void ephemeralOf(final String path, final IRecord rec) {
        final var json = rec.json();
        this.ephemeralOf(path, json);
    }

    /**
     * 路径上的数据
     * 
     * @param <U>    数据结果
     * @param path   路径
     * @param mapper 数据映射
     * @return 数据结果
     */
    default <U> U recordOf(final String path, final ExceptionalFunction<byte[], U> mapper) {
        U u = null;
        try {
            u = this.get_data_throws(path, mapper);
        } catch (Exception e) {
            //
        }
        return u;
    }

    /**
     * 获取路径上的数据
     * 
     * @param path 路径
     * @return 路径上的数据
     */
    default IRecord recordOf(final String path) {
        Map<?, ?> map = null;
        final var json = this.recordOf(path, String::new);
        final var objM = new ObjectMapper();
        if (json == null || json.matches("^\\s*$")) {
            return IRecord.REC();
        } else {
            try {
                map = objM.readValue(json, Map.class);
            } catch (Exception e) {
                System.err.println("error json for " + path + " : '" + json + "'");
                e.printStackTrace();
            }
            return IRecord.REC(map);
        } // if
    }

    /**
     * 设置 节点数的 数据内容
     * 
     * @param path 节点路径
     * @param data 数据内容
     */
    default void setData(final String path, final IRecord data) {
        try {
            this.set_data_throws(path, data);
        } catch (Exception e) {
            e.printStackTrace();
        } // try
    }

    /**
     * 节点是否存在
     * 
     * @param path 节点路径
     * @return 节点是否存在
     */
    default boolean exists(final String path) {
        boolean ret = false;

        try {
            ret = this.exists_throws(path);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ret;
    }

    /**
     * 
     * @param path
     */
    default void deleteIfExists(final String path) {
        if (this.exists(path)) {
            this.deletePath(path);
        }
    }

    /**
     * 
     * @param path
     * @return
     */
    default Boolean isEphemeral(final String path) {
        Boolean ret = null;
        try {
            ret = this.is_ephemeral_throws(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ret;
    }

    /**
     * 
     * @param path
     * @param mapper
     * @return
     * @throws Exception
     */
    default IRecord recordOfParent(final String path, final Function<byte[], IRecord> mapper) {

        IRecord rec = null;

        try {
            rec = this.get_parent_data_throws(path, bb -> {
                final var json = new String(bb);
                return Json.json2rec(json);
            }); // 父节点
        } catch (Exception e) {
            e.printStackTrace();
        } // try

        return rec;
    }

    /**
     * 节点数的根节点
     * 
     * @param path 节点路径
     * @return 节点数的根节点, 异常时候 返回null
     */
    default ZKTrieNode<String> getTree(final String path) {
        ZKTrieNode<String> root = null;
        try {
            root = this.get_tree_throws(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return root;
    }

    /**
     * 
     * @param path
     * @return
     */
    public static String getParent(final String path) {
        final var matcher = Pattern.compile("(.*)/[^/]+$").matcher(path);
        return !matcher.find() ? null : matcher.group(1);
    }
}
